import frappe
from frappe import _
from frappe.utils import flt, nowdate, get_link_to_form, date_diff
from erpnext.manufacturing.doctype.work_order.work_order import (
    WorkOrder,
    validate_operation_data,
    split_qty_based_on_batch_size,
    CapacityError,
    make_work_order as erpnext_make_work_order
)
import json
from light_mes.light_mes.doctype.subcontracting_service_price.subcontracting_service_price import get_subcontracting_price
from light_mes.light_mes.doctype.operation_price_strategy.operation_price_strategy import get_operation_price_for_bom

def create_job_card(work_order, row, enable_capacity_planning=False, auto_create=False):
    """重写创建加工单方法，增加自定义字段"""
    doc = frappe.new_doc("Job Card")
    doc.update(
        {
            "work_order": work_order.name,
            "workstation_type": row.get("workstation_type"),
            "operation": row.get("operation"),
            "workstation": row.get("workstation"),
            "posting_date": nowdate(),
            "for_quantity": row.get("job_card_qty") or work_order.get("qty", 0),
            "operation_id": row.get("name"),
            "bom_no": work_order.bom_no,
            "project": work_order.project,
            "company": work_order.company,
            "sequence_id": row.get("sequence_id"),
            "wip_warehouse": work_order.wip_warehouse or row.get("wip_warehouse")
            if not work_order.skip_transfer or work_order.from_wip_warehouse
            else work_order.source_warehouse or row.get("source_warehouse"),
            "hour_rate": row.get("hour_rate"),
            "serial_no": row.get("serial_no"),
            # 添加自定义字段
            "custom_enable_wip_receipt": row.get("custom_enable_wip_receipt"),
            "custom_is_subcontracting": row.get("custom_is_subcontracting"),
            "custom_supplier": row.get("custom_supplier"),
            "custom_service_price": row.get("custom_service_price"),
            "custom_plant_floor": work_order.get("custom_plant_floor"),
            "custom_rate_type": row.get("custom_rate_type")  # 添加计价方式字段
        }
    )

    if work_order.transfer_material_against == "Job Card" and not work_order.skip_transfer:
        doc.get_required_items()

    if auto_create:
        doc.flags.ignore_mandatory = True
        if enable_capacity_planning:
            doc.schedule_time_logs(row)

        doc.insert()
        frappe.msgprint(_("Job card {0} created").format(get_link_to_form("Job Card", doc.name)), alert=True)

    if enable_capacity_planning:
        # automatically added scheduling rows shouldn't change status to WIP
        doc.db_set("status", "Open")

    return doc

class CustomWorkOrder(WorkOrder):
    def set_work_order_operations(self):
        """重写设置生产工单工序方法，增加自定义字段"""
        def _get_operations(bom_no, qty=1):
            data = frappe.get_all(
                "BOM Operation",
                filters={"parent": bom_no},
                fields=[
                    "operation",
                    "description",
                    "workstation",
                    "idx",
                    "workstation_type",
                    "base_hour_rate as hour_rate",
                    "time_in_mins",
                    "parent as bom",
                    "batch_size",
                    "sequence_id",
                    "fixed_time",
                    # 添加自定义字段
                    "custom_is_subcontracting",
                    "custom_enable_wip_receipt",
                    "custom_supplier",
                    "custom_service_price",
                    "custom_tax_rate",  # 添加税率字段
                    "custom_service_price_with_tax",  # 添加含税单价字段
                    "custom_rate_type",  # 添加计价方式字段
                    "custom_labour_cost"  # 添加劳务费用字段
                ],
                order_by="idx",
            )

            for d in data:
                if not d.fixed_time:
                    d.time_in_mins = flt(d.time_in_mins) * flt(qty)
                d.status = "Pending"

            return data

        self.set("operations", [])
        if not self.bom_no or not frappe.get_cached_value("BOM", self.bom_no, "with_operations"):
            return

        operations = []

        if self.use_multi_level_bom:
            bom_tree = frappe.get_doc("BOM", self.bom_no).get_tree_representation()
            bom_traversal = reversed(bom_tree.level_order_traversal())

            for node in bom_traversal:
                if node.is_bom:
                    operations.extend(_get_operations(node.name, qty=node.exploded_qty / node.bom_qty))

        bom_qty = frappe.get_cached_value("BOM", self.bom_no, "quantity")
        operations.extend(_get_operations(self.bom_no, qty=1.0 / bom_qty))

        for correct_index, operation in enumerate(operations, start=1):
            operation.idx = correct_index

        self.set("operations", operations)
        self.calculate_time()
        
        # 计算所有工序的价格和成本
        self.calculate_operation_prices()

    def calculate_operation_prices(self):
        """为所有工序计算价格和成本"""
        if not self.company or not self.production_item or not self.qty:
            return
            
        
        total_subcontracting_cost = 0
        total_labour_cost = 0
            
        for operation in self.operations:
            # 根据计价类型计算单价和成本
            rate_type = operation.custom_rate_type
            
            if not rate_type:
                continue
                
            price = 0
            
            if rate_type == "Subcontracting":
                # 如果是委外类型，使用原有的委外单价计算逻辑
                if operation.custom_supplier:
                    price = get_subcontracting_price(
                        company=self.company,
                        supplier=operation.custom_supplier,
                        qty=self.qty,  # 使用工单数量
                        item_code=self.production_item,  # 使用工单物料
                        operation=operation.operation
                    )
                    
                    # 更新工序的委外单价
                    operation.custom_service_price = price
                    
                    # 如果有税率，计算含税单价
                    if operation.custom_tax_rate:
                        price_with_tax = flt(price * (1 + operation.custom_tax_rate / 100))
                        operation.custom_service_price_with_tax = price_with_tax
                    
                    # 计算委外成本
                    if price > 0:
                        subcontracting_cost = flt(price * self.qty)
                        operation.custom_planned_subcontracting_cost = subcontracting_cost
                        total_subcontracting_cost += subcontracting_cost
            
            elif rate_type in ["By Hour", "By Qty"]:
                # 准备工序对象，包含主表信息
                operation_data = {}
                # 复制工序对象的所有属性
                for key in dir(operation):
                    if not key.startswith('_') and not callable(getattr(operation, key)):
                        operation_data[key] = getattr(operation, key)
                
                # 添加主表信息
                parent_doc = {}
                for key in dir(self):
                    if not key.startswith('_') and not callable(getattr(self, key)):
                        parent_doc[key] = getattr(self, key)
                operation_data['parent_doc'] = parent_doc
                
                # 使用get_operation_price_for_bom获取价格
                price = get_operation_price_for_bom(
                    product=self.production_item,
                    operation=operation_data,  # 传递包含主表信息的工序对象
                    price_date=nowdate()
                )
                
                # 更新工序单价
                operation.custom_service_price = price
                
                # 计算劳务成本
                if price > 0:
                    if rate_type == "By Hour":
                        # 计时：成本 = 单价 * (时间分钟 / 60)
                        labour_cost = flt(price * operation.time_in_mins / 60)
                    else:  # By Qty
                        # 计件：成本 = 单价 * 数量
                        labour_cost = flt(price * self.qty)
                    
                    operation.custom_planned_labour_cost = labour_cost
                    total_labour_cost += labour_cost
        
        # 更新工单的总成本字段
        self.custom_planned_subcontracting_cost = total_subcontracting_cost
        self.custom_planned_labour_cost = total_labour_cost
 
        # 调用计算总运营成本的方法
        self.calculate_operating_cost()

    def prepare_data_for_job_card(self, row, index, plan_days, enable_capacity_planning):
        """重写准备加工单数据的方法"""
        self.set_operation_start_end_time(row, index)

        job_card_doc = create_job_card(
            self, row, auto_create=True, enable_capacity_planning=enable_capacity_planning
        )

        if enable_capacity_planning and job_card_doc:
            row.planned_start_time = job_card_doc.scheduled_time_logs[-1].from_time
            row.planned_end_time = job_card_doc.scheduled_time_logs[-1].to_time

            if date_diff(row.planned_end_time, self.planned_start_date) > plan_days:
                frappe.message_log.pop()
                frappe.throw(
                    _(
                        "Unable to find the time slot in the next {0} days for the operation {1}..."
                    ).format(plan_days, row.operation),
                    CapacityError,
                )

            row.db_update()

    def calculate_total_subcontracting_fee(self):
        """重新计算工单的累计委外加工费"""
        total_fee = 0
        
        # 遍历所有工序明细
        for operation in self.operations:
            # 累加每个工序的委外加工费
            if operation.custom_subcontracting_fee:
                total_fee += operation.custom_subcontracting_fee
        
        # 更新工单的委外加工费字段
        self.custom_subcontracting_fee = total_fee
        

    def calculate_operating_cost(self):
        """覆盖原始的操作成本计算方法，增加计划委外成本和计划人工成本"""
        # 初始化所有成本变量
        self.planned_operating_cost = 0.0 
        self.actual_operating_cost = 0.0
        
        # 初始化自定义字段，确保它们不是None
        self.custom_planned_subcontracting_cost = 0.0
        self.custom_planned_labour_cost = 0.0
        self.custom_subcontracting_fee = 0.0
        self.custom_actual_labour_cost = 0.0
        
        # 单次遍历工序表，计算所有计划成本
        for d in self.get("operations"):
            # 1. 计算标准操作成本
            d.planned_operating_cost = flt(
                flt(d.hour_rate) * (flt(d.time_in_mins) / 60.0), d.precision("planned_operating_cost")
            )
            d.actual_operating_cost = flt(
                flt(d.hour_rate) * (flt(d.actual_operation_time) / 60.0), d.precision("actual_operating_cost")
            )
            

            self.planned_operating_cost += flt(d.planned_operating_cost)
            self.actual_operating_cost += flt(d.actual_operating_cost)
            
            # 2. 累加委外成本和人工成本
            self.custom_planned_subcontracting_cost += flt(getattr(d, 'custom_planned_subcontracting_cost', 0))
            self.custom_subcontracting_fee += flt(getattr(d, 'custom_subcontracting_fee', 0))
            self.custom_planned_labour_cost += flt(getattr(d, 'custom_planned_labour_cost', 0))
            self.custom_actual_labour_cost += flt(getattr(d, 'custom_actual_labour_cost', 0))
        
        # 计算变动成本（使用实际成本或计划成本）
        variable_cost = (
            self.actual_operating_cost if self.actual_operating_cost else self.planned_operating_cost
        )
        
        # 使用自定义的委外成本和人工成本字段（优先使用实际数据，没有则使用计划数据）
        subcontracting_cost = flt(self.custom_subcontracting_fee) or flt(self.custom_planned_subcontracting_cost)
        labour_cost = flt(self.custom_actual_labour_cost) or flt(self.custom_planned_labour_cost)
        
        # 计算最终的总运营成本
        self.total_operating_cost = (
            flt(self.additional_operating_cost) + 
            flt(variable_cost) + 
            flt(self.corrective_operation_cost) + 
            flt(subcontracting_cost) + 
            flt(labour_cost)
        )


@frappe.whitelist()
def make_job_card(work_order, operations):
    if isinstance(operations, str):
        operations = json.loads(operations)

    work_order = frappe.get_doc("Work Order", work_order)
    for row in operations:
        row = frappe._dict(row)
        validate_operation_data(row)
        qty = row.get("qty")
        
        # 从工单工序明细中获取自定义字段
        operation_details = None
        for op in work_order.operations:
            if op.name == row.name:  # 使用 operation_id 匹配
                operation_details = op
                break
                
        if operation_details:
            # 添加自定义字段到 row
            row.update({
                "custom_enable_wip_receipt": operation_details.custom_enable_wip_receipt,
                "custom_is_subcontracting": operation_details.custom_is_subcontracting,
                "custom_supplier": operation_details.custom_supplier,
                "custom_service_price": operation_details.custom_service_price,
                "custom_rate_type": operation_details.custom_rate_type,
                "custom_plant_floor": work_order.custom_plant_floor
            })
        
        while qty > 0:
            qty = split_qty_based_on_batch_size(work_order, row, qty)
            if row.job_card_qty > 0:
                create_job_card(work_order, row, auto_create=True) 


@frappe.whitelist()
def make_work_order(bom_no, item, qty=0, project=None, variant_items=None, use_multi_level_bom=None):
    """
    重写标准的make_work_order函数增加自动设置车间的功能
    """
    # 调用原始函数创建工单
    wo_doc = erpnext_make_work_order(bom_no, item, qty, project, variant_items, use_multi_level_bom)
    
    # 从物料默认值表中获取默认车间
    if item and wo_doc.company:
        # 查询物料默认值表
        item_defaults = frappe.get_all(
            "Item Default",
            filters={
                "parent": item,
                "company": wo_doc.company
            },
            fields=["custom_default_plant_floor"]
        )
        
        # 如果找到默认车间，设置到工单中
        if item_defaults and item_defaults[0].get("custom_default_plant_floor"):
            wo_doc.custom_plant_floor = item_defaults[0].get("custom_default_plant_floor")
            
            # 如果车间有关联的仓库，也一并设置
            if wo_doc.custom_plant_floor:
                plant_floor = frappe.get_doc("Plant Floor", wo_doc.custom_plant_floor)
                
                # 设置在制品仓库
                if plant_floor.warehouse:
                    wo_doc.wip_warehouse = plant_floor.warehouse
                
                # 设置源仓库
                if hasattr(plant_floor, 'custom_default_source_warehouse') and plant_floor.custom_default_source_warehouse:
                    wo_doc.source_warehouse = plant_floor.custom_default_source_warehouse
                
                # 设置成品仓库
                if hasattr(plant_floor, 'custom_default_finished_goods_warehouse') and plant_floor.custom_default_finished_goods_warehouse:
                    wo_doc.fg_warehouse = plant_floor.custom_default_finished_goods_warehouse
                
                # 设置废料仓库
                if hasattr(plant_floor, 'custom_default_scrap_warehouse') and plant_floor.custom_default_scrap_warehouse:
                    wo_doc.scrap_warehouse = plant_floor.custom_default_scrap_warehouse
    
    return wo_doc